﻿/// ZaxControls
/// Programmed by Leland Ede
/// Website: http://www.zaxisstudios.com
/// E-Mail: codeplex@zaxis-studios.com
/// Create: 4/01/2014
/// Modified: 4/9/2014

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZaxLib.Web.Writers.Nodes
{
	public class JNode : INode
	{
		#region Class Data Structures

		private ArrayList children;
		private ArrayList parameters;

		#endregion

		#region Parameters

		private int _level = 0;
		public int Level
		{
			get
			{
				return _level;
			}
			set
			{
				_level = value;
			}
		}

		private String _code;
		public String Code
		{
			get
			{
				StringBuilder sb = new StringBuilder();
				switch (Type)
				{
					case NodeType.AJAX:
						{
							sb.Append(DrawAjax());
						}
						break;
					case NodeType.JOINED:
						{
							sb.Append(DrawJoined());
						}
						break;
					case NodeType.OBJECT:
						{
							sb.Append(DrawObject());
						}
						break;
					case NodeType.PARAMETER:
						{
							sb.Append(_code);
						}
						break;
					case NodeType.SELECTOR:
						{
							sb.Append(DrawSelector());
						}
						break;
					case NodeType.SELECTOR_CLASS:
						{
							sb.Append(DrawSelectorClass());
						}
						break;
					case NodeType.SELECTOR_FUNCTION:
						{
							sb.Append(DrawSelectorFunction());
						}
						break;
					case NodeType.SELECTOR_ID:
						{
							sb.Append(DrawSelectorId());
						}
						break;
					case NodeType.FUNCTION:
						{
							sb.Append(DrawFunction());
						}
						break;
					case NodeType.FUNCTION_NAME:
						{
							sb.Append(_code);
						}
						break;
					case NodeType.METHOD:
						{
							sb.Append(DrawMethod());
						}
						break;
					case NodeType.RAWCODE:
						{
							sb.Append(_code);
							sb.Append("\n");
							sb.Append('\t', Level);
							sb.Append(DrawChildren());
						}
						break;
					case NodeType.VARNAME:
						{
							sb.Append(DrawVar());
						}
						break;
					case NodeType.VARIABLE:
						{
							sb.Append(DrawChildren());
						}
						break;
					default:
						{
							if (HasChildren)
							{
								sb.Append(DrawChildren());
							}
						}
						break;
				}

				return sb.ToString();
			}
			set
			{
				_code = value;
			}
		}

		public String ParamSet
		{
			get
			{
				return String.Format("{0}: {1}", _code, Value);
			}
		}

		private String _value;
		public String Value
		{
			get
			{
				return _value;
			}
			set
			{
				_value = value;
			}
		}

		private INode _parent;
		public INode Parent
		{
			get
			{
				return _parent;
			}
			set
			{
				_parent = value;
			}
		}

		public INode FirstChild
		{
			get
			{
				INode first = null;
				if(children.Count > 0)
				{
					first = children[0] as INode;
				}
				return first;
			}
		}

		private NodeType _type;
		public NodeType Type
		{
			get
			{
				return _type;
			}
			set
			{
				_type = value;
			}
		}

		public bool HasChildren
		{
			get
			{
				return children.Count > 0;
			}
		}

		public bool HasParams
		{
			get
			{
				return parameters.Count > 0;
			}
		}

		#endregion

		#region Constructors
		
		public JNode(String inCode)
		{
			Code = inCode;
			Init();
		}

		public JNode(String inKey, String inValue)
		{
			Code = inKey;
			Value = inValue;
			Init();
		}

		public JNode()
		{
			Code = String.Empty;
			Init();
		}

		public void Init()
		{
			children = new ArrayList();
			parameters = new ArrayList();
			_parent = null;
		}

		#endregion

		#region Class Overrides

		public override string ToString()
		{
			return Code;
		}

		#endregion

		#region INode Methods

		public void AddChild(INode node)
		{
			children.Add(node);
		}

		public void AddParam(INode node)
		{
			parameters.Add(node);
		}

		public INode Ajax()
		{
			Type = NodeType.AJAX;
			return this;
		}
		
		public INode Array()
		{
			Type = NodeType.ARRAY;
			return this;
		}

		public INode Function()
		{
			Type = NodeType.FUNCTION;
			return this;
		}

		public INode FunctionName()
		{
			Type = NodeType.FUNCTION_NAME;
			return this;
		}

		public INode Joined()
		{
			Type = NodeType.JOINED;
			return this;
		}

		public INode Method()
		{
			Type = NodeType.METHOD;
			return this;
		}

		public INode ObjectDef()
		{
			Type = NodeType.OBJECT;
			return this;
		}

		public INode Param()
		{
			Type = NodeType.PARAMETER;
			return this;
		}

		public INode Selector()
		{
			Type = NodeType.SELECTOR;
			return this;
		}

		public INode SelectorClass()
		{
			Type = NodeType.SELECTOR_CLASS;
			return this;
		}

		public INode SelectorFunction()
		{
			Type = NodeType.SELECTOR_FUNCTION;
			return this;
		}

		public INode SelectorId()
		{
			Type = NodeType.SELECTOR_ID;
			return this;
		}

		public INode Top()
		{
			Type = NodeType.TOP;
			return this;
		}

		public INode RawCode()
		{
			Type = NodeType.RAWCODE;
			return this;
		}

		public INode VarDef()
		{
			Type = NodeType.VARNAME;
			return this;
		}

		public INode VarValue()
		{
			Type = NodeType.VARVALUE;
			return this;
		}

		public INode VarName()
		{
			Type = NodeType.VARNAME;
			return this;
		}

		public INode Variable()
		{
			Type = NodeType.VARIABLE;
			return this;
		}

		#endregion

		#region Draw Utilities

		private String DrawAjax()
		{
			StringBuilder sb = new StringBuilder();

			sb.Append("$.ajax");
			sb.Append(DrawParams());
			sb.Append(";\n");
			return sb.ToString();
		}

		private String DrawMethod()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append(_code);
			if (HasParams) sb.Append(DrawParams());
			if (HasChildren)
			{
				if (FirstChild.Type == NodeType.JOINED)
				{
					sb.Append(DrawChildren());
				}
				else
				{
					sb.Append('\t', Level);
					sb.Append("{\n");
					Level++;
					sb.Append(DrawChildren());
					Level--;
					sb.Append("\n");
					sb.Append('\t', Level);
					sb.Append("}");
				}
			}
			else
			{
				sb.Append(";\n");
			}
			return sb.ToString();
		}

		private String DrawObject()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("{");
			sb.Append(String.Join(",", parameters.ToArray()));
			sb.Append("}");

			return sb.ToString();
		}

		private String DrawFunction()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append(_code);
			if (FirstChild != null && FirstChild.Type == NodeType.FUNCTION_NAME)
			{
				sb.Append(" ");
				sb.Append(FirstChild.Code);
				children.Remove(FirstChild);
			}
			sb.Append(DrawParams());
			if (!HasParams)
			{
				sb.Append("()");
			}// Endif function doesn't have any parameters

			if (HasChildren)
			{
				sb.Append("\n");
				sb.Append('\t', Level);
				sb.Append("{\n");
				Level++;
				sb.Append('\t', Level);
				sb.Append(DrawChildren());
				Level--;
				if (sb.ToString()[sb.ToString().Length - 1] != '\n') sb.Append("\n");
				sb.Append('\t', Level);
				sb.Append("}");
			}
			else
			{
				sb.Append(";\n");
			}

			return sb.ToString();
		}

		private String DrawParams()
		{
			StringBuilder sb = new StringBuilder();
			if (HasParams)
			{
				sb.Append("(");
				sb.Append(String.Join(",", parameters.ToArray()));
				sb.Append(")");
			}
			return sb.ToString();
		}

		private String DrawChildren()
		{
			StringBuilder sb = new StringBuilder();
			if (HasChildren)
			{
				foreach (INode cNode in children)
				{
					cNode.Level = Level;
					sb.Append(cNode.Code);
				}
			}
			return sb.ToString();
		}

		private String DrawJoined()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append(".");
			sb.Append(_code);

			sb.Append(DrawParams());
			if (HasChildren)
			{
				sb.Append(DrawChildren());
			}
			else
			{
				sb.Append(";\n");
			}

			return sb.ToString();
		}

		private String DrawSelector()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("$(");
			sb.Append(_code);
			sb.Append(DrawParams());
			sb.Append(")");
			sb.Append(DrawChildren());
			return sb.ToString();
		}

		private String DrawSelectorClass()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("$(\".");
			sb.Append(_code);
			sb.Append("\")");
			sb.Append(DrawChildren());
			return sb.ToString();
		}

		private String DrawSelectorFunction()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("$(");
			sb.Append(DrawFunction());
			sb.Append(");\n");
			

			return sb.ToString();
		}

		private String DrawSelectorId()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("$(\"#");
			sb.Append(_code);
			sb.Append("\")");
			sb.Append(DrawChildren());
			return sb.ToString();
		}

		private String DrawVar()
		{
			StringBuilder sb = new StringBuilder();
			switch (Parent.Type)
			{
				case NodeType.OBJECT:
					{
						sb.Append(_code);
						sb.Append(": ");
						if (Value != null)
						{
							sb.Append(Value);
						}
						else if(HasChildren)
						{
							sb.Append(DrawChildren());
						}
					}
					break;
				case NodeType.ARRAY:
					{
						sb.Append(String.Format("var {0} = [", _code));
						sb.Append(String.Join(",", parameters.ToArray()));
						sb.Append("];\n");
						sb.Append('\t', Level);
					}
					break;
				case NodeType.PARAMETER:
					{
						sb.Append("var ");
						sb.Append(_code);
						sb.Append(" = ");
						sb.Append(Value);
						sb.Append(";\n");
					}
					break;
				case NodeType.VARIABLE:
					{
						sb.Append('\t', Level);
						sb.Append("var ");
						sb.Append(_code);
						sb.Append(" = ");
						sb.Append(Value);
						sb.Append(";\n");
					}
					break;
				case NodeType.JOINED:
				case NodeType.FUNCTION:
					{
						sb.Append(_code);
					}
					break;
			}

			return sb.ToString();
		}

		private String DrawVarDef()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append('\t', Level);
			sb.Append("var ");
			sb.Append(_code);
			sb.Append(" = ");
			sb.Append(Value);
			sb.Append(";\n");

			return sb.ToString();
		}
	
		#endregion

	}
}
